﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

using SmlEngine;
using SmlEngine.Sprites.Collections;
using SmlEngine.Sprites.Settings;
using SmlEngine.Sprites;
using SmlEngine.Sprites.Base.WorldMap;
using SmlEngine.UI;
using SmlEngine.UI.Input;
using SmlEngine.UI.Managers;
using SmlEngine.UI.Screens;

namespace Sml.Scenes
{
    /// <summary>
    /// Encapsulates and manages a group of worlds.
    /// </summary>
    public class WorldScene : IScene
    {
        #region Properties

        #region IScene Properties

        /// <summary>
        /// The screen that owns this scene.
        /// </summary>
        public Screen Screen { get; set; }

        /// <summary>
        /// The visible area of the screen.
        /// </summary>
        public Rectangle? Area { get; set; }

        private OverworldBorder border;

        /// <summary>
        /// Stores the debug state.
        /// </summary>
        public bool IsDebugging { get; set; }

        /// <summary>
        /// The file paths of the worlds that are loaded in this scene.
        /// </summary>
        public List<string> world_filePath { get; private set; }

        private int activeWorldIndex = 0;
        private int newWorldSection = -1;
        private WorldPlayer swapPlayer;
        private Vector2 swapDestination;

        #endregion
        /// <summary>
        /// The worlds loaded in this WorldScene.
        /// </summary>
        public List<World> Worlds { get; set; }
         
        #endregion

        #region Methods

        /// <summary>
        /// Constructs a new WorldScene instance.
        /// </summary>
        /// <param name="screen">The owner of this scene.</param>
        /// <param name="filePath">A list of file paths to the worlds of this scene.</param>
        /// <param name="screenSize">The size of the window or screen.</param>
        public WorldScene(Screen screen, List<string> filePath, Vector2 screenSize, int startWorldIndex)
        {
            Screen = screen;
            world_filePath = filePath;
            Worlds = new List<World>();
            border = new OverworldBorder(screenSize);
            for (int i = 0; i < filePath.Count; i++)
            {
                Worlds.Add(new World(screenSize));
                Worlds[i].Input = Screen.ScreenManager.Input;
                Worlds[i].Section = i;
                Worlds[i].fadeCompletedEvent += new FadeCompletedEventHandler(WorldScene_fadeCompletedEvent);
            }
            activeWorldIndex = startWorldIndex;
        }

        private void WorldScene_fadeCompletedEvent(object sender, FadeCompletedEventArgs e)
        {
            if (e.fadeType == FadeType.Out && newWorldSection != -1)
            {
                activeWorldIndex = newWorldSection;
                Worlds[activeWorldIndex].FadeIn();
                Worlds[activeWorldIndex].AddPlayer(swapPlayer, swapDestination);
                Worlds[activeWorldIndex].focusedPlayer = Worlds[activeWorldIndex].Players.IndexOf(swapPlayer);
                ((World)sender).Players.Remove(swapPlayer);
                ((World)sender).isCameraLocked = false;
                ((World)sender).UpdateFocusedPlayer();
                newWorldSection = -1;

                swapPlayer = null;
                swapDestination = new Vector2(float.NaN, float.NaN);
            }
        }

        /// <summary>
        /// Loads the worlds.
        /// </summary>
        /// <param name="cm">The ContentManager to use.</param>
        /// <param name="gd">The GraphicsDevice to use.</param>
        public void Load(ContentManager cm, GraphicsDevice gd)
        {
            Worlds[activeWorldIndex].Start();
            Worlds[activeWorldIndex].Load(world_filePath[0], cm, gd);
            Worlds[activeWorldIndex].LoadContent(cm, gd);
            Worlds[activeWorldIndex].FadeIn();
        }

        /// <summary>
        /// Loads a certain world.
        /// </summary>
        /// <param name="index">The index of the world to load.</param>
        /// <param name="cm">The ContentManager to use.</param>
        /// <param name="gd">The GraphicsDevice to use.</param>
        public void LoadWorld(int index, ContentManager cm, GraphicsDevice gd)
        {
            Worlds[index].Start();
            Worlds[index].Load(world_filePath[1], cm, gd);
            Worlds[index].LoadContent(cm, gd);
        }

        /// <summary>
        /// Initializes this WorldScene.
        /// </summary>
        public void Initialize()
        {
        }

        /// <summary>
        /// Loads the content for this WorldScene.
        /// </summary>
        /// <param name="content">The ContentManager to use.</param>
        public void LoadContent(ContentManager content)
        {
            border.LoadContent(content, Screen.ScreenManager.GraphicsDevice);
        }

        /// <summary>
        /// Updates the active world.
        /// </summary>
        /// <param name="gameTime">The timing information for the game.</param>
        /// <param name="input">The current keyboard/controller state for this frame.</param>
        public void Update(GameTime gameTime, InputManager input)
        {
            if (Worlds[activeWorldIndex].SwapSectionIndex != -1)
            {
                int index = Worlds[activeWorldIndex].SwapSectionIndex;
                var destination = Worlds[activeWorldIndex].SwapDestination;
                int player = Worlds[activeWorldIndex].SwapWorldPlayerIndex;

                Worlds[activeWorldIndex].ClearSwap();
                ChangeSection(activeWorldIndex, index, destination, player);
            }
            else
            {
                Worlds[activeWorldIndex].Update(gameTime);
            }
            border.LevelText = Worlds[activeWorldIndex].LevelDisplayText;
            border.LivesText = Worlds[activeWorldIndex].LivesDisplayText;
        }

        /// <summary>
        /// Draws the active world.
        /// </summary>
        /// <param name="gameTime">The timing information for the game.</param>
        /// <param name="spriteBatch">The SpriteBatch to draw onto.</param>
        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            Worlds[activeWorldIndex].Draw(gameTime, spriteBatch);
            border.Draw(gameTime, spriteBatch);
        }

        /// <summary>
        /// Starts debugging.
        /// </summary>
        public void Debug()
        {
            IsDebugging = true;
        }

        /// <summary>
        /// Reloads the content for the worlds.
        /// </summary>
        public void ReloadContent()
        {
            Worlds[activeWorldIndex].ReloadContent(Screen.ScreenManager.GraphicsDevice, Screen.ScreenManager.Game.Content);
        }

        /// <summary>
        /// Changes the active world.
        /// </summary>
        /// <param name="index">The index of the world to change to.</param>
        public void SwapWorld(int index)
        {
            if (index == activeWorldIndex)
            {
                Worlds[index].FadeIn(); 
                return;
            }
            if (!Worlds[index].IsLoaded)
            {
                LoadWorld(index, Screen.ScreenManager.Game.Content, Screen.ScreenManager.GraphicsDevice);
            }
            activeWorldIndex = index;
            Worlds[index].FadeIn();
        }

        public void ChangeSection(int oldSectionIndex, int newSectionIndex, Vector2 destination, int playerIndex)
        {
            if ((oldSectionIndex < Worlds.Count) && (Worlds[oldSectionIndex] != null))
            {
                if ((newSectionIndex < Worlds.Count) && (Worlds[newSectionIndex] != null))
                {
                    Worlds[newSectionIndex].Load(world_filePath[newSectionIndex], null, null);
                    PathTile p = (PathTile)Worlds[newSectionIndex].GetPath(destination.FloorDivide(16));
                    if (p == null || !p.IsRevealed) return;

                    WorldPlayer player = Worlds[oldSectionIndex].GetPlayer(playerIndex);
                    if (player != null)
                    {
                        newWorldSection = newSectionIndex;
                        Worlds[newSectionIndex].LoadContent(Screen.ScreenManager.Game.Content, Screen.ScreenManager.GraphicsDevice);
                        swapPlayer = player;
                        swapDestination = destination;
                        Worlds[oldSectionIndex].isCameraLocked = true;
                        Worlds[oldSectionIndex].FadeOut();
                    }
                }
            }
        }
        #endregion
    }
}
